// Json.js: Query Json plugin - http://code.google.com/p/jquery-json/

(function ($) {
    var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        s = {
            'array': function (x) {
                var a = ['['], b, f, i, l = x.length, v;
                for (i = 0; i < l; i += 1) {
                    v = x[i];
                    f = s[typeof v];
                    if (f) {
                        v = f(v);
                        if (typeof v == 'string') {
                            if (b) {
                                a[a.length] = ',';
                            }
                            a[a.length] = v;
                            b = true;
                        }
                    }
                }
                a[a.length] = ']';
                return a.join('');
            },
            'boolean': function (x) {
                return String(x);
            },
            'null': function (x) {
                return "null";
            },
            'number': function (x) {
                return isFinite(x) ? String(x) : 'null';
            },
            'object': function (x) {
                if (x) {
                    if (x instanceof Array) {
                        return s.array(x);
                    }
                    var a = ['{'], b, f, i, v;
                    for (i in x) {
                        v = x[i];
                        f = s[typeof v];
                        if (f) {
                            v = f(v);
                            if (typeof v == 'string') {
                                if (b) {
                                    a[a.length] = ',';
                                }
                                a.push(s.string(i), ':', v);
                                b = true;
                            }
                        }
                    }
                    a[a.length] = '}';
                    return a.join('');
                }
                return 'null';
            },
            'string': function (x) {
                if (/["\\\x00-\x1f]/.test(x)) {
                    x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
                        var c = m[b];
                        if (c) {
                            return c;
                        }
                        c = b.charCodeAt();
                        return '\\u00' +
                            Math.floor(c / 16).toString(16) +
                            (c % 16).toString(16);
                    });
                }
                return '"' + x + '"';
            }
        };

	$.toJSON = function(v) {
		var f = isNaN(v) ? s[typeof v] : s['number'];
		if (f) return f(v);
	};
	
	$.parseJSON = function(v, safe) {
		if (safe === undefined) safe = $.parseJSON.safe;
		if (safe && !/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(v))
			return undefined;
		return eval('('+v+')');
	};
	
	$.parseJSON.safe = false;

})(jQuery);

// " - // this quote is to keep textmate from thinking the rest of this is inside quotes. The regex above confuses it.

// Any changes to this block of code. And you'll have to restart Firefox to get it to work properly, because of the event handlers.
var ubiq_omni = {
	globals: { initialized: false, prefName: "extensions.ubiquity.omni.searchEngines" },
	
	// Load the onLoad event handler into each tab. I've played around with remove/add and it appears that sometimes if you don't do a remove it will add it twice.
	init: function() { 
		Application.activeWindow.tabs.forEach(function(tab) { 
			tab.events.removeListener('load',ubiq_omni.onLoad); // Remove the old one... Just to make sure we don't duplicate.
			tab.events.addListener('load',ubiq_omni.onLoad); // Add it back
			if(ubiq_omni.globals.initialized == false) { ubiq_omni.findSearchForm(tab.document) } // If it's the first time make sure we check each current document for a form.
		});

		ubiq_omni.globals.initialized = true;
	},
	
	// Simply waits until the page has been refreshed, and then checks the new page for a search form.
	onLoad: function () { 
    	var doc = Application.activeWindow.activeTab.document;
    	ubiq_omni.findSearchForm(doc.body);
  	},

	// Checks each form element for something that "looks" like a search form. Then binds an onsubmit handler to that form.
	findSearchForm: function (context) {
		jQuery('form', context).each(function(i) {
			var searchForm = false;
			
			if(jQuery(this).find('input[type=submit]').attr('value') == "Search") { searchForm = true; }
			if(searchForm == true) 
			{ 
				jQuery(this).unbind('submit',ubiq_omni.addSearchEngine);
				jQuery(this).submit(ubiq_omni.addSearchEngine);
			}
		});
	},

	// Gets called whenever a search form is submitted. It mostly gets the data together, so that the engineManager can add the search engine.
	addSearchEngine: function () {
		var domain = Application.activeWindow.activeTab.document.domain;
		var domain_parts = domain.split('.');
		var engine_name = domain_parts[domain_parts.length-2]; // Should be google, yahoo, etc.
		if(domain_parts[0] != 'www') { engine_name += " " + domain_parts[0]; } // Hopefully we end up with google groups, mozilla developers, etc.
		
		// At the moment this just takes the first text input field and uses it to build the searchURL. Ideally it should "replicate" the form.
		
		var searchInput = jQuery(this).find('input[type=text]').attr('name');
		searchInput += "={QUERY}";
		
		var searchURL = this.action;
		if(searchURL.match(/\?/)) { searchURL += "&" + searchInput; }
		else { searchURL += "?" + searchInput; }
		
		ubiq_omni.engineManager_add(engine_name,searchURL);
	},
	
	// Pulls the list of search engines. Takes an optional piece of text to search against.
		engineManager_get: function(fragment) {
		var pm = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch); // Firefox Preference Manager
		
		if(pm.prefHasUserValue(ubiq_omni.globals.prefName)) { // Check to see if the preference exists
			var engines = jQuery.parseJSON(pm.getCharPref(ubiq_omni.globals.prefName)); // The preference is just a Javascript object converted to a JSON string, so we need to convert it back.
			if(fragment) // Filter the search engine list 
			{
				var filtered = new Object();
				for(en in engines)
				{
					if(en.match(fragment)) { filtered[en] = engines[en]; }
				}
				return filtered;
			} else { 
				return engines; 
			}
		} 
		else {
			return new Object(); // No preference exists, so just return an empty object.
		}
	},
	
	// Checks to see if this engine is already added, if not it adds it.
	engineManager_add: function(engine_name,searchURL) {
		var pm = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch) // Firefox Preference Manager
		engines = ubiq_omni.engineManager_get(); 
		
		if(!engines[engine_name]) {  
			engines[engine_name] = { searchURL: searchURL, popularity: 1 }; // popularity starts at one.
			pm.setCharPref(ubiq_omni.globals.prefName,jQuery.toJSON(engines)); // Convert the object to JSON and save it in the firefox preferences.
			displayMessage("Found new search engine: " + engine_name); // Inform the user that we've found a new search engine.
		}
	},
};

// Call omni.init... Will be called everytime Ubiquity is invoked. 
ubiq_omni.init();
Application.activeWindow.events.addListener("TabOpen", ubiq_omni.init ); // Make sure we call it on new tabs as well.

// The noun type, mainly provides us with the suggestions list.
var noun_type_omniengine = {
	_name: "omni search engine",
	suggest: function(fragment) {
		var suggestions = [];
    	var engines = ubiq_omni.engineManager_get(fragment);
	
    	for(en in engines) {
			suggestions.push(CmdUtils.makeSugg(en, null, en))
		}
    	return suggestions;
	},
      
	getDefault: function() {
   
  	}
};

// The actual omni command.
CmdUtils.CreateCommand({
  name: "omni",
  icon: "chrome://ubiquity/content/icons/search.png",
  description: "Search using discovered search engines",
  takes: {query: noun_arb_text},
  modifiers: {"with": noun_type_omniengine},
  preview: function(previewBlock, inputObject, queryModifiers) {
    
  },
  execute: function(inputObject, queryModifiers) {
	engine_name = queryModifiers.with.text;
    var engines = ubiq_omni.engineManager_get(engine_name);
	var searchURL = engines[engine_name].searchURL;
	
	searchURL = searchURL.replace('{QUERY}',inputObject.text);
	
	Utils.openUrlInBrowser(searchURL);
  }
});